package com.acc.mobileappr.webservice;

import com.acc.mobileappr.constant.AppConstant;
import com.acc.mobileappr.entities.MstMApprDocProcessEntity;
import com.acc.mobileappr.entities.MstMApprUserEntity;
import com.acc.mobileappr.entities.TrnMApprRequestEntity;
import com.acc.mobileappr.util.PushUtil;
import com.acc.mobileappr.util.Util;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.platform.database.jdbc.JDBCTypes;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLStoredProcedureCall;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.Session;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Common web service
 * Abiel@ICN
 */
@WebService()
public class UserService {

    private static EntityManagerFactory factory;
    //private static final Logger logger = Logger.getLogger(UserService.class);

    /**
     * Constructor
     */
    public UserService() {
        Util.print("User Service Created");
        factory = Persistence.createEntityManagerFactory(AppConstant.PERSISTENCE_UNIT_MAREX);
    }


    /**
     * Login
     *
     * @param userName User name
     * @param pin      User pin
     * @param password USer Password
     * @return OK = Login OK
     *         FAIL-USERNOTFOUND = User not found
     *         FAIL-WRONGPASSWORD = Wrong password
     *         FAIL-INTERNALERROR = Internal Error
     */
    @WebMethod(operationName = "login")
    public String login(@WebParam(name = "userName") String userName, @WebParam(name = "pin") String pin, @WebParam(name = "password") String password) {

        Util.print("Login called");
        Util.print("userName= " + userName);
        Util.print("pin= " + pin);
        Util.print("password= " + password);

        //check validity of user name and password via database
        EntityManager em;
        em = factory.createEntityManager();

        try {
            Query q = em.createQuery("select u from MstMApprUserEntity u where lower(u.userName)= ?1 and lower(u.userPin)= ?2")
                    .setParameter(1, userName.toLowerCase())
                    .setParameter(2,pin.toLowerCase());

            List<MstMApprUserEntity> users = q.getResultList();

            if (users.isEmpty()) {
                em.close();
                return AppConstant.LOGIN_FAIL_NOUSER;
            } else {
                Util.print("User Found");

                MstMApprUserEntity user = users.get(0);
                Util.print("User Id= " + user.getUserId()
                        + ", User Name= " + user.getUserName()
                        + ", User Pin= " + user.getUserPin());

                if (!user.getPassword().equals(password)) {
                    Util.print("BUT the password is WRONG, goddammit!");
                    em.close();
                    return AppConstant.LOGIN_FAIL_WRONGPASSWORD;
                } else {
                    Util.print("Login accepted");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            em.close();
            return AppConstant.LOGIN_FAIL_INTERNAL_ERROR;
        }
        em.close();
        return "OK";
    }

    /**
     * Reset Password
     *
     * @param userName user name
     * @param pin      user pin
     * @return OK = Login OK
     *         FAIL-USERNOTFOUND = User not found
     *         FAIL-INTERNALERROR = Internal Error
     */
    @WebMethod(operationName = "resetPassword")
    public String resetPassword(@WebParam(name = "userName") String userName, @WebParam(name = "pin") String pin) {
        Util.print("Reset Password called");
        Util.print("userName= " + userName);
        Util.print("pin= " + pin);


        //check user validity
        //check validity of user name and password via database
        EntityManager em;
        em = factory.createEntityManager();

        try {
            Query q = em.createQuery("select u from MstMApprUserEntity u where lower(u.userName)= ?1 and lower(u.userPin)= ?2")
                             .setParameter(1, userName.toLowerCase())
                             .setParameter(2,pin.toLowerCase());

            List<MstMApprUserEntity> users = q.getResultList();

            if (users.isEmpty()) {
                em.close();
                return AppConstant.LOGIN_FAIL_NOUSER;
            } else {
                 Util.print("User Found");

                MstMApprUserEntity user = users.get(0);
                Util.print("User Id= " + user.getUserId()
                        + " User Name= " + user.getUserName()
                        + " User Pin= " + user.getUserPin());

                //resetting
                em.getTransaction().begin();
                user.setPassword(AppConstant.getDefaultPassword());
                user.setPasswordStatus("N");
                em.persist(user);
                em.getTransaction().commit();

            }
        } catch (Exception e) {
            e.printStackTrace();
            em.close();
            return AppConstant.LOGIN_FAIL_INTERNAL_ERROR;
        }

        em.close();
        //reset password
        return "OK";
    }

    /**
     * Change Password
     *
     * @param userName    user name
     * @param pin         user BB pin
     * @param password    user password
     * @param newPassword user new password
     * @return OK = Login OK
     *         FAIL-USERNOTFOUND = User not found
     *         FAIL-WRONGPASSWORD = Wrong password
     *         FAIL-INTERNALERROR = Internal Error
     */

    @WebMethod(operationName = "changePassword")
    public String changePassword(@WebParam(name = "userName") String userName, @WebParam(name = "pin") String pin
            , @WebParam(name = "password") String password, @WebParam(name = "newPassword") String newPassword) {

        Util.print("Change Password called");
        Util.print("userName= " + userName
                + " pin= " + pin
                + " password= " + password
                + " newPassword= " + newPassword);

        //check user validity
        EntityManager em;
        em = factory.createEntityManager();

        try {
            Query q = em.createQuery("select u from MstMApprUserEntity u where lower(u.userName)= ?1 and lower(u.userPin)= ?2")
                             .setParameter(1, userName.toLowerCase())
                             .setParameter(2,pin.toLowerCase());

            List users = q.getResultList();

            if (users.isEmpty()) {
                em.close();
                return AppConstant.LOGIN_FAIL_NOUSER;
            } else {

                Util.print("User Found");

                MstMApprUserEntity user = (MstMApprUserEntity) users.get(0);

                Util.print(user.toString());

                //resetting
                em.getTransaction().begin();
                user.setPassword(newPassword);
                user.setPasswordStatus("Y");
                em.persist(user);
                em.getTransaction().commit();

            }
        } catch (Exception e) {
            e.printStackTrace();
            em.close();
            return AppConstant.LOGIN_FAIL_INTERNAL_ERROR;
        }

        //change password

        return "OK";
    }


    /**
     * Approve or Reject process
     *
     * @param docId          Document Id/Type
     * @param registrationNo Registration no
     * @param approvalStatus Approval status (A=Active, R=Rejected)
     * @param userPin        User BB Pin
     * @param password       User Password
     * @param comment        Comment to be added
     * @return OK = Login OK
     *         FAIL-USERNOTFOUND = User not found
     *         FAIL-INTERNALERROR = Internal Error
     */
    @WebMethod(operationName = "doApproveOrReject")
    public String doApproveOrReject(@WebParam(name = "docId") String docId
            , @WebParam(name = "registrationNo") String registrationNo
            , @WebParam(name = "approvalStatus") String approvalStatus
            , @WebParam(name = "userPin") String userPin
            , @WebParam(name = "password") String password
            , @WebParam(name = "comment") String comment) {

        /**
         * tables :
         * trn_m_appr_request -> mst_m_appr_action =>  (doc_id -> doc_id)
         *
         */
        String checkVal = checkPinExistOrRightPassword(userPin, password);
        Util.print("doApproveOrReject:_");
        Util.print("checking on pin or password \nRegistration No= "+registrationNo
                 +"\n docId="+docId
                 +"\n approvalStatus="+approvalStatus
                 +"\n userPin="+userPin
                 +"\n password="+password
                 +"\n comment="+comment);


        if (!checkVal.equals(AppConstant.LOGIN_OK)) {
            System.out.println(checkVal);
            return checkVal;
        }

        EntityManager em;
        em = factory.createEntityManager();

        Query q = em.createQuery("select p from MstMApprDocProcessEntity p where p.docId = ?1")
                .setParameter(1, docId);
        List lstProcess = q.getResultList();

        String processName;

        for ( Object process : lstProcess) {
            processName = ((MstMApprDocProcessEntity)process).getProcessName();
            //run procedure with input: p_regno, p_userpin , p_action, p_comment , param out p_result and p_statement

            try {
                JpaEntityManager jpaEntityManager = JpaHelper.getEntityManager(em);
                Session session = jpaEntityManager.getActiveSession();

                PLSQLStoredProcedureCall call = new PLSQLStoredProcedureCall();
                call.setProcedureName("ACC_BBMX."+processName);
                call.addNamedArgument("p_regno", JDBCTypes.VARCHAR_TYPE);
                call.addNamedArgument("p_userpin", JDBCTypes.VARCHAR_TYPE);
                call.addNamedArgument("p_action", JDBCTypes.VARCHAR_TYPE);
                call.addNamedArgument("p_comment", JDBCTypes.VARCHAR_TYPE);
                call.addNamedOutputArgument("p_result", JDBCTypes.VARCHAR_TYPE);
                call.addNamedOutputArgument("p_statement", JDBCTypes.VARCHAR_TYPE);

                DataReadQuery query = new DataReadQuery();
                query.addArgument("p_regno");
                query.addArgument("p_userpin");
                query.addArgument("p_action");
                query.addArgument("p_comment");
                query.addArgument("p_result");
                query.addArgument("p_statement");

                query.setCall(call);

                List<String> queryArgs = new ArrayList<String>();
                queryArgs.add(registrationNo);
                queryArgs.add(userPin);
                queryArgs.add(approvalStatus);
                queryArgs.add(comment);
                queryArgs.add("");
                queryArgs.add("");

                List results = (List) session.executeQuery(query, queryArgs);
                DatabaseRecord record = (DatabaseRecord) results.get(0);
                String result = (String) record.get("p_result");
                String statement = (String) record.get("p_statement");

                Util.print("Result="+result);
                Util.print("Statement="+statement);
            } catch (Exception e) {
                e.printStackTrace();
                Util.error(e.getLocalizedMessage());
            }

        }
        //push to device with

        em.close();
        return AppConstant.APPROVE_OK;
    }

    /**
     * Request approved records by pushing it to device
     *
     * @param userPin       user BB pin
     * @param password      user password
     * @param dateFrom      request from date
     * @param dateTo        request until date
     * @param docId         document id
     * @return              Formatted Records
     */
    @WebMethod(operationName = "requestApprovedRecords")
    public String requestApprovedRecords(@WebParam(name = "userPin") String userPin
            , @WebParam(name = "password") String password
            , @WebParam(name = "dateFrom") String dateFrom
            , @WebParam(name = "dateTo") String dateTo
            , @WebParam(name = "docId") String docId) {
        /**
         *  Steps
         *
         *  1. A= query TrnMApprRequest where status = 'A'
         *
         */
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
        Date xDateFrom;
        Date xDateTo;
        PushUtil pushUtil = new PushUtil();

        try {
            Util.print("requestApprovedRecords:_");
            xDateFrom = formatter.parse(dateFrom);
            xDateTo = formatter.parse(dateTo);

            String checkVal = checkPinExistOrRightPassword(userPin, password);

            if (!checkVal.equals(AppConstant.LOGIN_OK)) {
                Util.print(checkVal);
                return checkVal;
            }

            EntityManager em;
            em = factory.createEntityManager();
            Query q = em.createQuery("select r from TrnMApprRequestEntity r where r.actionStatus='A' and (r.actionDate between ?1 and ?2) and r.docId=?3 ")
                    .setParameter(1, xDateFrom)
                    .setParameter(2, xDateTo)
                    .setParameter(3, docId);

            List<TrnMApprRequestEntity> lst = q.getResultList();

            if (lst.isEmpty()) {
                return "FAIL_NO_DATA";
            }

            int x = 1;
            int n=lst.size();

            for (TrnMApprRequestEntity req : lst) {
                Util.print("UserService.requestApprovedRecords:Sending request document no "+x+" of "+n);
                pushUtil.pushData(userPin, req.getPushId(), req.getData());
                x++;
            }


        } catch (ParseException e) {
            e.printStackTrace();
            Util.error(e.getLocalizedMessage());
            return "FAIL_INTERNAL_ERROR";
        }

        return "OK";
    }


    /**
     * check whether a user is existed or password is right
     *
     * @param userName user name
     * @param password user password
     * @return   string
     */
    private String checkUserExistOrRightPassword(String userName, String password) {

        EntityManager em;
        em = factory.createEntityManager();
        String retval = "";
        try {

            Query q = em.createQuery("select u from MstMApprUserEntity u where lower(u.userName) = ?1")
                    .setParameter(1, userName.toLowerCase());
            
            List<MstMApprUserEntity> users = q.getResultList();

            if (users.isEmpty()) {
                retval = AppConstant.LOGIN_FAIL_NOUSER;
            }

            MstMApprUserEntity user = users.get(0);
            if (!user.getPassword().equals(password)) {
                retval = AppConstant.LOGIN_FAIL_WRONGPASSWORD;
            }
        } catch (Exception e) {
            e.printStackTrace();
            Util.error(e.getLocalizedMessage());
            retval = AppConstant.LOGIN_FAIL_INTERNAL_ERROR;

        } finally {
            em.close();
        }

        return retval;
    }


    /**
     * check whether a user is existed or password is right
     *
     * @param userPin  user BB pin
     * @param password user password
     * @return         string
     */
    private String checkPinExistOrRightPassword(String userPin, String password) {

        EntityManager em;
        em = factory.createEntityManager();
        Query q = em.createQuery("select u from MstMApprUserEntity u where lower(u.userPin) = ?1").setParameter(1, userPin.toLowerCase());
        List<MstMApprUserEntity> users = q.getResultList();

        if (users.isEmpty()) {
            em.close();
            return AppConstant.LOGIN_FAIL_NOUSER;
        }

        MstMApprUserEntity user = users.get(0);
        if (!user.getPassword().toLowerCase().equals(password.toLowerCase())) {
            em.close();
            return AppConstant.LOGIN_FAIL_WRONGPASSWORD;
        }

        em.close();
        return AppConstant.LOGIN_OK;
    }

}
